package com.lanyou.esb.service;

import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.lanyou.cook.common.Constants;
import com.lanyou.cook.datadictionary.UserLogOpt;
import com.lanyou.cook.entity.domain.ParamSetting;
import com.lanyou.cook.paramsetting.ParamSettingReppository;
import com.lanyou.cook.service.UserLogService;
import com.lanyou.cook.util.gson.GsonUtil;
import com.lanyou.esb.common.CommonUtils;
import com.lanyou.esb.datadictionary.BundleActionType;
import com.lanyou.esb.datadictionary.BundleType;
import com.lanyou.esb.entity.DefinedBundle;
import com.lanyou.esb.exception.BundleActionException;
import com.lanyou.esb.exception.ServerMixException;
import com.lanyou.esb.exception.ServiceHandlerException;
import com.lanyou.esb.repository.DefinedBundleRepository;
import com.lanyou.esb.servicemix.bean.Bundle;
import com.lanyou.esb.servicemix.monitor.ESBServer;
import com.lanyou.esb.servicemix.monitor.ESBServerFactory;
import com.lanyou.esb.servicemix.monitor.rest.MonitorService;
import com.lanyou.esb.servicemix.monitor.rest.bean.BundleResponse;
import com.lanyou.esb.servicemix.util.BundleHandleUtil;
import com.lanyou.esb.servicemix.util.ConnetServerTest;
import com.lanyou.esb.util.FileDelCilent;
import com.lanyou.esb.web.param.BaseServiceManagementParam;

/**
 * 基础服务管理<br>
 * 
 * 
 * @author Davey.wu
 */
@Service
public class BaseServiceManagementService {
	@Autowired
	private ESBServerFactory esbServerFactory;

	@Autowired
	private DefinedBundleService definedBundleService;

	@Autowired
	private DefinedBundleRepository definedBundleRepository;

	@Autowired
	private ParamSettingReppository paramSettingReppository;

	@Autowired
	private ServiceManagerService serviceManagerService;

	@Autowired
	private UserLogService userLogService;

	/**
	 * 根据条件获取Bundle,分页
	 * 
	 * @param param
	 * @param pageable
	 * @return
	 * @throws BundleActionException
	 */
	public Page<Bundle> findBundleInfos(BaseServiceManagementParam param,
			Pageable pageable) throws BundleActionException {
		List<Bundle> bundles = findSystemLogLevelInfos(param);
		// 无记录，返回空集
		if (bundles == null || bundles.isEmpty()) {
			return new PageImpl<Bundle>(new ArrayList<>(0), pageable, 0);
		}
		// 分页，第一个BUNDLE下标
		int startIndex = pageable.getPageNumber() * pageable.getPageSize();
		// 开始下标超出记录数，返回空集
		if (startIndex >= bundles.size()) {
			return new PageImpl<Bundle>(new ArrayList<>(0), pageable, 0);
		}
		// 分页，最后一个BUNDLE下标
		int toIndex = startIndex + pageable.getPageSize();
		// 最后下标不能超出记录数
		toIndex = toIndex < bundles.size() ? toIndex : bundles.size();
		// 总记录数
		int total = bundles.size();
		// 根据开始、结束下标，取当前页的数据
		List<Bundle> result = bundles.subList(startIndex, toIndex);
		List<String> codeList = new ArrayList<>(result.size());
		// 收集目标BUNDLE标识
		for (Bundle bundle : result) {
			codeList.add(bundle.getId() + "");
		}
		// 根据目标BUNDLE标识，在Bundles表中查询相应的记录
		List<DefinedBundle> definedBundles = definedBundleService
				.getDefinedBundleByCode(codeList);

		Map<String, String> map = new HashMap<>();
		if (definedBundles != null) {
			for (DefinedBundle definedBundle : definedBundles) {
				map.put(definedBundle.getBundleCode(), definedBundle.getType());
			}
		}
		// 设置BUNDLE类型
		for (Bundle bundle : result) {
			String type = map.get(bundle.getId() + "");
			// 在BUNDLES表中没有相应的记录，则为核心BUNDLE
			if (StringUtils.isBlank(type)) {
				type = BundleType.CORE.ValueCode;
			}
			bundle.setType(type);
		}
		return new PageImpl<Bundle>(result, pageable, total);
	}

	/**
	 * 根据条件获取Bundle
	 * 
	 * @param param
	 * @return
	 * @throws BundleActionException
	 */
	public List<Bundle> findSystemLogLevelInfos(BaseServiceManagementParam param)
			throws BundleActionException {
		BundleResponse bundleResponse = null;
		try {
			List<MonitorService> monitorService = getServiceMixServer();
			bundleResponse = monitorService.get(0).getBundles();
		} catch (Exception e) {
			if (e.getCause() instanceof ConnectException) {
				throw new BundleActionException("连接ServiceMix引擎失败", 0);
			} else {
				throw new BundleActionException(e.getMessage(), 0);
			}
		}
		List<Bundle> bundles = bundleResponse.getData();
		if (bundles == null) {
			return new ArrayList<>(0);
		}
		int size = bundles.size();
		for (int i = size - 1; i >= 0; --i) {
			Bundle bundle = bundles.get(i);
			if (!compare(bundle, param)) {
				bundles.remove(i);
			}
		}
		Collections.sort(bundles, new Comparator<Bundle>() {

			@Override
			public int compare(Bundle o1, Bundle o2) {
				// TODO Auto-generated method stub
				return o1.getId() > o2.getId() ? -1
						: (o1.getId() < o2.getId() ? 1 : 0);
			}

		});
		return bundles;
	}

	/**
	 * 对比Bundle属性是否符合指定条件
	 * 
	 * @param bundle
	 * @param param
	 * @return
	 */
	private boolean compare(Bundle bundle, BaseServiceManagementParam param) {
		if (StringUtils.isNotBlank(param.getBundleName())) {
			if (StringUtils.isBlank(bundle.getName())) {
				return false;
			}
			if (!bundle.getName().toUpperCase()
					.contains(param.getBundleName().toUpperCase())) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 启动Bundle
	 * 
	 * @param bundleId
	 * @throws BundleActionException
	 */
	public void startBundle(String bundleId) throws BundleActionException {
		executeBundle(bundleId, BundleActionType.START.ValueName);
		// 处于Installed状态的Bundle启动未必成功，但也没返回任何错误消息，因此，请求启动后还需要判断是否真的启动
		Bundle bundle = getBundleById(Integer.parseInt(bundleId));
		if (bundle.getStateRaw() != 32) {
			throw new BundleActionException("启动Boundle失败，具体失败原因，请查看日志！", 0);
		}
		userLogService.save(UserLogOpt.MaintenanceBaseService, null,
				String.format("启动Bundle“%s”", bundle.getName()));
	}

	/**
	 * 根据ID获取Bundle
	 * 
	 * @param id
	 * @return
	 * @throws BundleActionException
	 */
	public Bundle getBundleById(int id) throws BundleActionException {
		BundleResponse bundleResponse = null;
		try {
			List<MonitorService> monitorService = getServiceMixServer();
			bundleResponse = monitorService.get(0).getBundleById(id);
		} catch (Exception e) {
			if (e.getCause() instanceof ConnectException) {
				throw new BundleActionException("连接ServiceMix引擎失败", 0);
			} else {
				throw new BundleActionException(e.getMessage(), 0);
			}
		}
		List<Bundle> boundles = bundleResponse.getData();
		if (boundles == null || boundles.isEmpty()) {
			throw new BundleActionException("未找到指定Id的Boundle", 0);
		}
		Bundle bundle = boundles.get(0);
		return bundle;
	}

	/**
	 * 停止Bundle
	 * 
	 * @param bundleId
	 * @throws BundleActionException
	 */
	public void stopBundle(String bundleId) throws BundleActionException {
		executeBundle(bundleId, BundleActionType.STOP.ValueName);
		Bundle bundle = getBundleById(Integer.parseInt(bundleId));
		userLogService.save(UserLogOpt.MaintenanceBaseService, null,
				String.format("停止Bundle“%s”", bundle.getName()));
	}

	/**
	 * 卸载Bundle
	 * 
	 * @param bundleId
	 * @throws BundleActionException
	 */
	@Transactional
	public void uninstallBundle(String bundleId) throws BundleActionException {
		try {
			DefinedBundle definedBundle = definedBundleRepository
					.findByBundleCode(bundleId);
			if (definedBundle == null) {
				throw new BundleActionException("找不到ID为【" + bundleId
						+ "】的Bundle！", 0);
			}
			// CookProxy不能被卸载
			if (CommonUtils.COOK_PROXY_BUNDLE_NAME.equals(definedBundle
					.getBundleName())) {
				throw new BundleActionException("CookProxy不能卸载！", 0);
			}
			// 基础服务只能卸载基础BUNDLE
			if (definedBundleRepository.findByBundleCode(bundleId) != null
					&& !BundleType.BASE.ValueCode.equals(definedBundle
							.getType())) {
				if (BundleType.CORE.ValueCode.equals(definedBundle.getType())) {
					throw new BundleActionException("内核Bundle不能卸载！", 0);
				}
				if (BundleType.SELFDEFINED.ValueCode.equals(definedBundle
						.getType())) {
					throw new BundleActionException("非内核Bundle只能在业务服务管理界面修改！",
							0);
				}

			}
			// 卸载BUNDLE
			executeBundle(bundleId, BundleActionType.UNINSTALL.ValueName);
			// 删除记录
			definedBundleRepository.delete(Arrays.asList(definedBundle));

			userLogService.save(UserLogOpt.MaintenanceBaseService, null, String
					.format("卸载Bundle“%s”", definedBundle.getBundleName()));
		} catch (Exception e) {
			if (e.getCause() instanceof ConnectException) {
				throw new BundleActionException("连接ServiceMix引擎失败", 0);
			} else {
				throw new BundleActionException(e.getMessage(), 0);
			}
		}
	}

	/**
	 * 操作Bundle
	 * 
	 * @param bundleId
	 * @param action
	 */
	private void executeBundle(String bundleId, String action)
			throws BundleActionException {
		if (StringUtils.isBlank(bundleId)) {
			throw new BundleActionException("BundleId不能为空", 0);
		}
		int id = -1;
		try {
			id = Integer.parseInt(bundleId);
		} catch (Exception e) {
			throw new BundleActionException("BundleId只能是整数", 0);
		}

		try {
			List<MonitorService> monitorServices = getAllServiceMixServer();
			for (MonitorService monitorService : monitorServices) {
				monitorService.executeBundle(id, action);
			}
		} catch (Exception e) {
			if (e.getCause() instanceof ConnectException) {
				throw new BundleActionException("连接ServiceMix引擎失败", 0);
			} else {
				throw new BundleActionException(e.getMessage(), 0);
			}
		}
	}

	/**
	 * 安装或更新Bundle
	 * 
	 * @param file
	 * @param name
	 * @param start
	 * @param startLevel
	 * @return 1:代表安装；2：代表更新
	 * @throws Exception
	 */
	public int installOrUpdateBundle(String fileName, boolean start,
			String startLevel) throws Exception {
		if (StringUtils.isBlank(fileName)) {
			throw new BundleActionException("文件名不能为空", 0);
		}
		if (fileName.indexOf(".jar") <= 0) {
			throw new BundleActionException("只能上传*.jar类型的文件", 0);
		}
		int level = 80;
		if (StringUtils.isNotBlank(startLevel)) {
			try {
				level = Integer.parseInt(startLevel);
			} catch (Exception e) {
				throw new BundleActionException("启动级别只能为正整数", 0);
			}
		}
		String startType = "1";
		if (!start) {
			startType = "0";
		}
		// 获取JAR的名称
		String jarName = getName(fileName);
		List<String> types = new ArrayList<>(2);
		types.add(BundleType.BASE.ValueCode);
		types.add(BundleType.CORE.ValueCode);
		// 根据JAR包获取BUNDLE信息
		List<DefinedBundle> list = definedBundleService.getDefinedBundle(null,
				jarName, types);
		// 不存在这个JAR包的BUNDLE，安装BUNDLE
		if (list == null || list.isEmpty()) {
			// 安装BUNDLE
			return installBundle(fileName, startType, level);
		}
		DefinedBundle bundle = list.get(0);
		// 判断这个JAR包是否是核心BUNDLE,核心BUNDLE不能重新安装，也不能更新
		if (BundleType.CORE.ValueCode.equals(bundle.getType())) {
			throw new BundleActionException("已存在JAR包为" + jarName
					+ "的核心BUNDLE,不能再安装或更新！", 0);
		}
		// 更新BUNDLE
		return updateBundle(bundle.getBundleCode(), fileName);
	}

	/**
	 * 获取文件名，不包含版本号
	 * 
	 * @param fileName
	 * @return
	 */
	private static String getName(String fileName) {
		String value = fileName.substring(0, fileName.indexOf("."));
		String reg = "-[0-9]*$";
		Matcher match = Pattern.compile(reg).matcher(value);
		if (match.find()) {
			return value.substring(0, value.lastIndexOf("-"));
		}
		return value;
	}

	/**
	 * 安装Bundle
	 * 
	 * @param fileName
	 *            ：文件名
	 * @param start
	 *            ：是否启动
	 * @param level
	 *            ：启动等级
	 * @return
	 * @throws Exception
	 */
	private int installBundle(String fileName, String start, int level)
			throws Exception {
		ParamSetting paramSettingServer = paramSettingReppository
				.findByParamName(Constants.SERVERMIX_SERVER);
		
		JsonArray jsonDatas = GsonUtil.getDefaultJsonParser()
				.parse(paramSettingServer.getParamValue()).getAsJsonArray();
		// 测试服务器是否可以连通
		try {
			ConnetServerTest.telnetConnect(esbServerFactory.getESBServer());
		} catch (ServerMixException e) {
			throw new ServiceHandlerException(e.getMessage(),
					CommonUtils.CODE_CONNECT_SERVICEMIX_FAIED);
		}
		String installedBundle = "";
		String errorMsg = "";
		for (JsonElement jsonElement : jsonDatas) {
			String bundlePath = ServiceManagerService.getRelativeFileNamePath(fileName, Constants.INSTALL_BUNDLE_SERVER_PATH);
			String ip = jsonElement.getAsJsonObject().get("ip").getAsString();
			String port = jsonElement.getAsJsonObject().get("port").getAsString();
			String socketDelPort = jsonElement.getAsJsonObject().get("SocketDelPort").getAsString();
			try {
				String bundleRequestAddress = "http://" + ip + ":" + port + Constants.BUNDLE_REQUEST_ADDRESS;
				// 安装bundle
				installedBundle = BundleHandleUtil.installBundle(
						fileName,
						bundlePath,
						start,
						bundleRequestAddress);
			} catch (Exception e) {
				errorMsg = "更新Bundle失败，详细信息：" + e.getMessage();
				// throw new ServiceHandlerException("安装Bundle失败，详细信息："
				// + e.getMessage(), 0);
			} finally {
				try {
					// 删除JAR文件
					if(!FileDelCilent.delBundleBySocket(ip, socketDelPort, fileName)){
						throw new ServiceHandlerException("安装失败，从 【ip=" + ip + "】 这台Servicemix服务器上删除已安装的接口部署包失败",0);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		if (StringUtils.isNotBlank(errorMsg)) {
			throw new ServiceHandlerException(errorMsg, 0);
		}
		if (StringUtils.isNotBlank(installedBundle)) {
			JsonObject json = GsonUtil.getDefaultJsonParser()
					.parse(installedBundle).getAsJsonObject();
			// 根据bundleId调用旧封装的api得到bundle详细信息（主要是用来获取Bundle名称）
			List<ESBServer> esbServers = esbServerFactory.getESBServer();
			Bundle bundle = esbServers.get(0).getRestFactory()
					.getMonitorService()
					.getBundleById(json.get("id").getAsInt()).getData().get(0);

			DefinedBundle definedBundle = new DefinedBundle();
			definedBundle.setBundleLocation(fileName);
			definedBundle.setBundleCode(String.valueOf(bundle.getId()));
			definedBundle.setBundleName(bundle.getName());
			definedBundle.setVersion(bundle.getVersion());
			definedBundle.setStatus(bundle.getState());
			definedBundle.setType(BundleType.BASE.ValueCode);
			definedBundle.setBundleJarName(fileName);
			definedBundle.setIsDefinedBundle("0");
			// 保存更新后的记录
			definedBundleRepository.save(Arrays.asList(definedBundle));

			userLogService.save(UserLogOpt.MaintenanceBaseService, null, String
					.format("安装Bundle“%s”", definedBundle.getBundleName()));
		}

		return 1;
	}

	/**
	 * 更新Bundle
	 * 
	 * @param id
	 *            :Bundle标识
	 * @param fileName
	 *            ：文件名
	 * @return
	 * @throws Exception
	 */
	private int updateBundle(String id, String fileName) throws Exception {
		ParamSetting paramSettingServer = paramSettingReppository
				.findByParamName(Constants.SERVERMIX_SERVER);
		
		if(paramSettingServer == null){
			//抛出异常
			throw new ServiceHandlerException(CommonUtils.MSG_NONE_SERVICE_MIX_SETTING,
					CommonUtils.CODE_NONE_SERVICE_MIX_SETTING);
		}
		
		JsonArray jsonDatas = GsonUtil.getDefaultJsonParser()
				.parse(paramSettingServer.getParamValue()).getAsJsonArray();
		// 测试服务器是否可以连通
		try {
			ConnetServerTest.telnetConnect(esbServerFactory.getESBServer());
		} catch (ServerMixException e) {
			throw new ServiceHandlerException(e.getMessage(),
					CommonUtils.CODE_CONNECT_SERVICEMIX_FAIED);
		}
		String installedBundle = "";
		// 错误消息
		String errorMsg = null;
		for (JsonElement jsonElement : jsonDatas) {
			String ip = jsonElement.getAsJsonObject().get("ip").getAsString();
			String port = jsonElement.getAsJsonObject().get("port").getAsString();
			String socketDelPort = jsonElement.getAsJsonObject().get("SocketDelPort").getAsString();
			
			String bundlePath = ServiceManagerService.getRelativeFileNamePath(fileName,Constants.INSTALL_BUNDLE_SERVER_PATH);

			try {
				// 安装bundle
				String bundleRequestAddress = "http://" + ip + ":" + port + Constants.BUNDLE_REQUEST_ADDRESS;
				
				installedBundle = BundleHandleUtil.updateBundle(
						id,
						bundlePath,
						bundleRequestAddress);
			} catch (Exception e) {
				errorMsg = "更新Bundle失败，详细信息：" + e.getMessage();
				// throw new ServiceHandlerException("更新Bundle失败，详细信息："
				// + e.getMessage(), 0);

			} finally {
				try {
					// 删除JAR文件
					FileDelCilent.delBundleBySocket(ip, socketDelPort, fileName);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

		}
		if (StringUtils.isNotBlank(errorMsg)) {
			throw new ServiceHandlerException(errorMsg, 0);
		}
		// 无BUNDLE信息返回,退出
		if (StringUtils.isBlank(installedBundle)) {
			return 1;
		}
		JsonObject json = GsonUtil.getDefaultJsonParser()
				.parse(installedBundle).getAsJsonObject();
		// 根据bundleId调用旧封装的api得到bundle详细信息
		List<ESBServer> esbServers = esbServerFactory.getESBServer();
		Bundle bundle = esbServers.get(0).getRestFactory().getMonitorService()
				.getBundleById(json.get("id").getAsInt()).getData().get(0);
		if (bundle == null) {
			return 1;
		}

		// 根据BUNDLE编码获取BUNDLE信息
		DefinedBundle definedBundle = definedBundleRepository
				.findByBundleCode(bundle.getId() + "");
		if (definedBundle == null) {
			return 1;
		}
		definedBundle.setBundleLocation(fileName);
		definedBundle.setBundleName(bundle.getName());
		definedBundle.setVersion(bundle.getVersion());
		definedBundle.setStatus(bundle.getState());
		definedBundle.setBundleJarName(fileName);
		// 保存更新后的记录
		definedBundleRepository.save(Arrays.asList(definedBundle));
		userLogService.save(UserLogOpt.MaintenanceBaseService, null,
				String.format("更新Bundle“%s”", definedBundle.getBundleName()));
		return 1;
	}

	private List<MonitorService> getServiceMixServer()
			throws ServerMixException {
		List<ESBServer> servers = esbServerFactory.getESBServer();
		if (servers == null || servers.isEmpty()) {
			throw new ServerMixException("连接ServiceMix引擎失败", 0);
		}
		List<MonitorService> result = new ArrayList<>();
		for (ESBServer server : servers) {
			if (ConnetServerTest.telnetConnect(server.getServerAddres(),
					server.getPort() + "")) {
				result.add(server.getRestFactory().getMonitorService());
			}
		}
		if (result.size() == 0) {
			throw new ServerMixException("连接ServiceMix引擎失败", 0);
		}
		return result;
	}

	private List<MonitorService> getAllServiceMixServer()
			throws ServerMixException {
		List<ESBServer> servers = esbServerFactory.getESBServer();
		if (servers == null) {
			throw new ServerMixException("连接ServiceMix引擎失败", 0);
		}
		// 检测是否能连接所有的SERVICEMIX服务器
		ConnetServerTest.telnetConnect(servers);
		List<MonitorService> result = new ArrayList<>();
		for (ESBServer server : servers) {
			result.add(server.getRestFactory().getMonitorService());
		}
		if (result.size() == 0) {
			throw new ServerMixException("连接ServiceMix引擎失败", 0);
		}
		return result;
	}

}
